﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;

namespace Common.OpenXml
{
	/// <summary>
	/// Stores all of the shared strings while the document is being built.  When building
	/// is done, all the strings can be written to the package at one time.
	/// </summary>
    public class SharedStrings
    {
        private readonly Dictionary<string, int> m_sharedStrings =
            new Dictionary<string, int>();
        private readonly Dictionary<int, string> m_IndexedStrings = new Dictionary<int, string>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="package"></param>
        public SharedStrings(PackageHelper package)
        {
            // define the uri's for later use
            Uri workbookUri =
                new Uri("/xl/workbook.xml", UriKind.Relative);
            Uri sharedStringsUri =
                new Uri(string.Format("/xl/sharedStrings.xml"), UriKind.Relative);

            // read the shared strings part into an XmlDocument
            XmlDocument sharedStringsXml =
                package.GetWritablePart(sharedStringsUri);

            if (sharedStringsXml != null)
            {
                int idx = 0;
                foreach (XmlNode siNode in sharedStringsXml.DocumentElement.ChildNodes)
                {
                    if (siNode.ChildNodes == null || siNode.ChildNodes.Count != 1)
                        continue;

                    XmlNode tNode = siNode.ChildNodes[0];
                    string s = tNode.InnerText;
                    m_sharedStrings.Add(s, idx);
                    m_IndexedStrings.Add(idx, s);
                    idx++;
                }
            }
            else
            {
                // create the new shared strings part
                package.CreateNewPart(sharedStringsUri,
                    "application/vnd.openxmlformats-" +
                    "officedocument.spreadsheetml.sharedStrings+xml",
                    Properties.Resources.SharedStringsTemplate);

                // relate the part to the workbook
                package.CreateInternalRelationship(
                    workbookUri, sharedStringsUri,
                    "http://schemas.openxmlformats.org/" +
                    "officeDocument/2006/relationships/sharedStrings");

                m_sharedStrings = new Dictionary<string, int>();
                m_IndexedStrings = new Dictionary<int, string>();
            }
        }

        /// <summary>
        /// Looks up the index of a string in the shared strings table.
        /// </summary>
        /// <param name="text">The text to look for.</param>
        /// <returns>The index of the string in the shared strings table.</returns>
        public int LookupString(string text)
        {
            // if the item is in the table, return its index
            if (m_sharedStrings.ContainsKey(text))
                return m_sharedStrings[text];

            // if it's not in the list, add it and return the index
            int newIndex = m_sharedStrings.Count;
            m_sharedStrings.Add(text, newIndex);
            m_IndexedStrings.Add(newIndex, text);
            return newIndex;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public string GetString(int index)
        {
            if (m_IndexedStrings.ContainsKey(index))
                return m_IndexedStrings[index];
            return null;
        }

        /// <summary>
        /// Saves the existing shared strings to the package.
        /// </summary>
        /// <param name="package">The package to save the strings to.</param>
        public void Save(PackageHelper package)
        {
            Uri sharedStringsUri =
                new Uri(string.Format("/xl/sharedStrings.xml"), UriKind.Relative);
            // read the shared strings part into an XmlDocument
            XmlDocument sharedStringsXml =
                package.GetWritablePart(sharedStringsUri);
            // write the new data to the shared strings table
            while (sharedStringsXml.DocumentElement.ChildNodes.Count > 0)
                sharedStringsXml.DocumentElement.RemoveChild(sharedStringsXml.DocumentElement.ChildNodes[0]);

            XPathNavigator sharedStringsNav =
                sharedStringsXml.DocumentElement.CreateNavigator();
            using (XmlWriter writer = sharedStringsNav.AppendChild())
            {
                // write each shared string
                foreach (KeyValuePair<string, int>
                    sharedString in m_sharedStrings)
                {
                    // write the string item
                    writer.WriteStartElement("si", Namespaces.SpreadsheetML);
                    writer.WriteElementString("t", Namespaces.SpreadsheetML,
                        sharedString.Key);
                    writer.WriteEndElement();
                }
            }

            // save the changes to the part
            package.SavePart(sharedStringsUri, sharedStringsXml);
        }
    }
}
